/**
 *
 * populates the nodeDomain
 * NodeReader - reads the node data and creates the corresponding data structures to represent these nodes
 * NodeList - contains the list of nodes and their attributes
 * NodeGraph - node dependencies - nodes and their relationships with each other
 * NodeTimeline - the temporal representation of the nodes
 *
 */
class NodeDomainReader
{
    static Logger logger = Logger.getLogger()


    /*
     * reads the node 
     * read the row that represents the node and fill its attributes
     */
    static def readNodeList(RowDataReader rowDataReader)
    {
        def nodeList = new NodeObjList()

        // read the node list
        logger.info("Reading Node List")
        logger.info("Columns:"+rowDataReader.getFieldNames())

        rowDataReader.iterate({ row -> 
            //println row.lastLineNumber() + ". Node:" + row.getValueByLabel("Node") + " of type:" + row.getValueByLabel("Type") + "->"+row.getValueByLabel("Attr1") + "," + row.getValueByLabel("Attr2")

            NodeObj node = new NodeObj(Utils.readTrimmedStr(row.getValueByLabel("Node")), row.getValueByLabel("Type"))
            def fieldNames = row.getLabels()
            fieldNames.each {
                if(!it.equalsIgnoreCase("Node") && !it.equalsIgnoreCase("Type"))
                {
                    node.attrs.put(it, Utils.readTrimmedStr(row.getValueByLabel(it)))
                }
            }

            nodeList.nodes.put(node.name, node)
            logger.info("\t|--" + row.lastLineNumber() + ". added Node:"+node)
        })

        return nodeList
    }

    /**
     * reads the styles of the nodes - fill color, shape etc.
     */
    static def readNodeStyles(NodeObjList nodeList, RowDataReader rowDataReader)
    {
        NodeStyleList nodeStyleList = new NodeStyleList()

        logger.info("Reading Node Styles")
        logger.info("Columns:"+rowDataReader.getFieldNames())

        rowDataReader.iterate({ row ->
            //println row.lastLineNumber() + ". Node:" + Utils.readTrimmedStr(row.getValueByLabel("Node")) + " Shape:" + Utils.readTrimmedStr(row.getValueByLabel("Node")) + " and FillColor:" + Utils.readTrimmedStr(row.getValueByLabel("Node"))

            def nodeName = Utils.readTrimmedStr(row.getValueByLabel("Node"))

            NodeStyle nodeStyle = new NodeStyle(nodeName: Utils.readTrimmedStr(row.getValueByLabel("Node")),
                                                    shape: Utils.readTrimmedStr(row.getValueByLabel("Shape")),
                                                    fillColor: Utils.readTrimmedStr(row.getValueByLabel("Fill Color")),
                                                    lineColor: Utils.readTrimmedStr(row.getValueByLabel("Line Color")),
                                                    mode: Utils.readTrimmedStr(row.getValueByLabel("Mode")))

            //nodeStyleList.nodeStyles.put(nodeStyle.nodeName, nodeStyle)
            def nodeObj = nodeList.nodes.get(nodeStyle.nodeName)
            if(nodeObj!=null)
            {
                nodeObj.nodeStyle = nodeStyle
            }
            else
            {
                // print out error that the node was not found in the master list
                logger.info("\t|--"+row.lastLineNumber() + ". ERROR: in reading Node style - Node name not found in master list: " + nodeName)
                
                // but anyways, if not found in master list, create new one and add the style to it
                nodeObj = new NodeObj(nodeName, nodeName)
                nodeObj.nodeStyle = nodeStyle
                nodeList.nodes.put(nodeName, nodeObj)                
            }

            logger.info("\t|--" + row.lastLineNumber() + ". added Node Style:" + nodeStyle)
        })

        return nodeStyleList
    }

    /*
     * reads the node groups
     * 
     */
    static def readNodeGroupList(NodeObjList nodeList, RowDataReader rowDataReader)
    {
        NodeGroupList nodeGroupList = new NodeGroupList()

        logger.info("Reading Node Groups")
        logger.info("Columns:"+rowDataReader.getFieldNames())

        rowDataReader.iterate({ row ->
            //println row.lastLineNumber() + ". " + Utils.readTrimmedStr(row.getValueByLabel("Group")) + " and Node:" + Utils.readTrimmedStr(row.getValueByLabel("Node"))

            // now either find an existing node group or initialize a new one
            def nodeGroup
            def name = Utils.readTrimmedStr(row.getValueByLabel("Group"))

            if(nodeGroupList.nodeGroups.get(name)!=null)
                nodeGroup = nodeGroupList.nodeGroups.get(name)
            else
                nodeGroup = new NodeGroup(name:name)

            // add the node name to the list of nodes within the group
            nodeGroup.nodeNames.add(Utils.readTrimmedStr(row.getValueByLabel("Node")))

            nodeGroupList.nodeGroups.put(nodeGroup.name, nodeGroup)

            logger.info("\t|--" + row.lastLineNumber() + ". added Node Group:"+nodeGroup)
        })

        return nodeGroupList
    }


    /**
     * read the node graph
     */
    static def readNodeGraph(NodeObjList nodeList, RowDataReader rowDataReader)
    {
        def nodeGraph = new NodeGraph()
        
        // now read the node graph        
        logger.info("Reading node dependencies:")
        logger.info("FieldNames:"+rowDataReader.getFieldNames())

        rowDataReader.iterate({ row -> 

                // println row.lastLineNumber() + ". " + row.getValueByLabel("Edge Caption")+":"+row.getValueByLabel("Start Node")+"->"+row.getValueByLabel("End Node")
                NodeEdge nodeEdge = new NodeEdge()

                nodeEdge.caption =  Utils.readTrimmedStr(row.getValueByLabel("Edge Caption"))
                nodeEdge.nodeStartName = Utils.readTrimmedStr(row.getValueByLabel("Start Node"))
                nodeEdge.nodeEndName =  Utils.readTrimmedStr(row.getValueByLabel("End Node"))

                // first check if the nodes exist in the master list
                if(nodeList.nodes[nodeEdge.nodeStartName] == null)
                {
                    // print out error
                    logger.info("\t|--"+row.lastLineNumber() + ". ERROR: in reading graph - Start Node '" + nodeEdge.nodeStartName + "' was not found in Node list. End Node was:"+nodeEdge.nodeEndName)
                    
                    // but add a new node anyways
                    def nodeObj = new NodeObj(nodeEdge.nodeStartName, nodeEdge.nodeStartName)
                    nodeList.nodes.put(nodeEdge.nodeEndName, nodeObj)                
                }
                else if(nodeList.nodes[nodeEdge.nodeEndName] == null)
                {
                    logger.info("\t|--"+row.lastLineNumber() + ". ERROR: in reading graph - End Node '" + nodeEdge.nodeEndName + "' was not found in Node list. Start Node was:"+nodeEdge.nodeStartName)

                    // but add a new node anyways
                    nodeObj = new NodeObj(nodeEdge.nodeEndName, nodeEdge.nodeEndName)
                    nodeList.nodes.put(nodeEdge.nodeEndName, nodeObj)                
                }

                nodeGraph.nodeEdges.add(nodeEdge)
                logger.info("\t|--"+ row.lastLineNumber() + ". added NodeEdge:"+nodeEdge)
        })

        return nodeGraph
    }


    /**
     * reads the node timeline
     */
    static def readNodeTimeline(String domainName, NodeObjList nodeList, RowDataReader rowDataReader)
    {
        def nodeTimeline = new NodeTimeline()
        
        // now read the node graph        
        logger.info("Reading node timeline:")
        logger.info("Field names:"+rowDataReader.getFieldNames())

        rowDataReader.iterate({ row -> 
                //println row.lastLineNumber() + ". " + row.getValueByLabel("Node")+":"+row.getValueByLabel("Start Date")+":"+row.getValueByLabel("End Date")

                String nodeName = Utils.readTrimmedStr(row.getValueByLabel("Node"))
                if(nodeName.size()>0)
                {
                    EventPt eventPt = NodeEventPtsUtil.getEventPtFromStrings(domainName,
                                                                            nodeName,
                                                                            row.getValueByLabel("Start Date"), 
                                                                            row.getValueByLabel("Start Time"), 
                                                                            row.getValueByLabel("End Date"), 
                                                                            row.getValueByLabel("End Time"), 
                                                                            row.getValueByLabel("Is Duration"), 
                                                                            row.getValueByLabel("Is Exact"))

                    // first check if the nodes exist in the master list
                    if(nodeList.nodes[nodeName] != null)
                    {
                        nodeList.nodes[nodeName].eventPt = eventPt
                        logger.info("\t|--"+row.lastLineNumber() + ". " + nodeName + ": added Timeline:" + eventPt)
                    }
                    else
                    {
                        logger.info("\t|--"+row.lastLineNumber() + ". " + ": ERROR: in reading timeline - Node '" + nodeName + "' was not found in master list.")
                    }
                }
                else
                {
                    logger.info("\t|--"+row.lastLineNumber() + ". " + ": ERROR: in reading timeline - Node '" + nodeName + "' was not found in master list.")
                }
        })

        return nodeTimeline
    }


    /**
     * This initializes the domain from the domain folder which could be local or remote
     * - it reads the files from the domain folder which could be local or on http
     * - the CSV files are named by convention rather than configuration
     */
    static NodeDomain initFromDomainFolder(String domainName, String sourceDocsLocation, mylogger)
    {
        if(mylogger!=null)
            logger = mylogger

        NodeDomain nodeDomain = new NodeDomain(domainName, sourceDocsLocation)
        RowDataSource dataSource
        RowDataReader rowDataReader

        println sourceDocsLocation+"/data/"+nodeDomain.name+"_list.csv"

        if(Utils.fileExistsLocalOrRemote(sourceDocsLocation+"/data/"+nodeDomain.name+"_list.csv"))
        {
            dataSource = new RowDataSource(sourceDocsLocation+"/data/"+nodeDomain.name+"_list.csv", "CSV")
            rowDataReader = new CSVDataReader(dataSource)
            nodeDomain.nodeList = NodeDomainReader.readNodeList(rowDataReader)
        }
        else
        {
            logger.error("The file "+ nodeDomain.name + "_list.csv : list of nodes was not found")
            throw new MyException("The Master list of nodes was not found")
        }

        if(Utils.fileExistsLocalOrRemote(sourceDocsLocation+"/data/"+nodeDomain.name+"_styles.csv"))
        {
            dataSource = new RowDataSource(sourceDocsLocation+"/data/"+nodeDomain.name+"_styles.csv", "CSV")
            rowDataReader = new CSVDataReader(dataSource)
            nodeDomain.nodeStyleList = NodeDomainReader.readNodeStyles(nodeDomain.nodeList, rowDataReader)
        }
        else
        {
            // continue
            logger.error("The file "+ nodeDomain.name+ "_styles.csv : styles of nodes was not found")
        }

        if(Utils.fileExistsLocalOrRemote(sourceDocsLocation+"/data/"+nodeDomain.name+"_groups.csv"))
        {
            dataSource = new RowDataSource(sourceDocsLocation+"/data/"+nodeDomain.name+"_groups.csv", "CSV")
            rowDataReader = new CSVDataReader(dataSource)
            nodeDomain.nodeGroupList = NodeDomainReader.readNodeGroupList(nodeDomain.nodeList, rowDataReader)
        }
        else
        {
            // continue
            logger.error("The file "+ nodeDomain.name+ "_groups.csv : groups of nodes was not found")
        }

        if(Utils.fileExistsLocalOrRemote(sourceDocsLocation+"/data/"+nodeDomain.name+"_graph.csv"))
        {
            dataSource = new RowDataSource(sourceDocsLocation+"/data/"+nodeDomain.name+"_graph.csv", "CSV")
            rowDataReader = new CSVDataReader(dataSource)
            nodeDomain.nodeGraph = NodeDomainReader.readNodeGraph(nodeDomain.nodeList, rowDataReader)
        }
        else
        {
            logger.error("The file "+ nodeDomain.name+ "_graph.csv : graph of nodes was not found")       
        }

        if(Utils.fileExistsLocalOrRemote(sourceDocsLocation+"/data/"+nodeDomain.name+"_timeline.csv"))
        {
            dataSource = new RowDataSource(sourceDocsLocation+"/data/"+nodeDomain.name+"_timeline.csv", "CSV")
            rowDataReader = new CSVDataReader(dataSource)
            nodeDomain.nodeTimeline = NodeDomainReader.readNodeTimeline(nodeDomain.name, nodeDomain.nodeList, rowDataReader)
        }
        else
        {
            logger.error("The file "+ nodeDomain.name+ "_timeline.csv : timeline of nodes was not found")
        }

        return nodeDomain
    }


    /**
     * reads the generic example files and creates the node graph for the domain
     */
    static NodeDomain initFromGenericExample()
    {
        String domainName = "MyDomain"

        String docsLocation_local = "../../MyDomain.source"
        String docsLocation_remote = "http://localhost:8080/timegraph/MyDomain.source"
        String docsLocation = docsLocation_local

        NodeDomain nodeDomain = NodeDomainReader.initFromDomainFolder(domainName, docsLocation, null)

        return nodeDomain
    }


    /**
     *
     */
    static void main(args)
    {
        // GENERIC BUILT-IN EXAMPLE
        NodeDomain nodeDomain = NodeDomainReader.initFromGenericExample()
        
        // REMOTE EXAMPLE
        //NodeDomain nodeDomain = NodeDomainReader.initFromDomainFolder("MyDomain", "http://localhost:8080/timegraph/MyDomain.source")
        
        // LOCAL EXAMPLE
        //NodeDomain nodeDomain = NodeDomainReader.initFromDomainFolder("MyDomain.backup", "./MyDomain.backup")
    }
}
